import type { NonEmptyArray } from 'utils/type.utils';
import {
    formatCveDiscoveredTime,
    getEarliestDiscoveredAtTime,
    getHighestCvssScore,
    getHighestVulnerabilitySeverity,
    getIsSomeVulnerabilityFixable,
} from './vulnerabilityUtils';

const CRITICAL = 'CRITICAL_VULNERABILITY_SEVERITY';
const IMPORTANT = 'IMPORTANT_VULNERABILITY_SEVERITY';
const MODERATE = 'MODERATE_VULNERABILITY_SEVERITY';
const LOW = 'LOW_VULNERABILITY_SEVERITY';
const UNKNOWN = 'UNKNOWN_VULNERABILITY_SEVERITY';

function makeVuln(severity, fixedByVersion, cvss, scoreVersion) {
    return { severity, fixedByVersion, cvss, scoreVersion };
}

describe('vulnerability utils', () => {
    describe('getHighestVulnerabilitySeverity', () => {
        it('returns the highest severity of any vulnerability in the component array', () => {
            const vulnerabilities = [makeVuln(LOW, '', 0, '')];
            expect(getHighestVulnerabilitySeverity(vulnerabilities)).toEqual(LOW);

            vulnerabilities.push(makeVuln(MODERATE, '', 0, ''));

            expect(getHighestVulnerabilitySeverity(vulnerabilities)).toEqual(MODERATE);

            vulnerabilities.push(makeVuln(IMPORTANT, '', 0, ''));

            expect(getHighestVulnerabilitySeverity(vulnerabilities)).toEqual(IMPORTANT);

            vulnerabilities.push(makeVuln(CRITICAL, '', 0, ''));

            expect(getHighestVulnerabilitySeverity(vulnerabilities)).toEqual(CRITICAL);

            const allSeverityVulns = [
                makeVuln('bogus', '', 0, ''),
                makeVuln(UNKNOWN, '', 0, ''),
                makeVuln(CRITICAL, '', 0, ''),
                makeVuln(IMPORTANT, '', 0, ''),
                makeVuln(MODERATE, '', 0, ''),
                makeVuln(LOW, '', 0, ''),
            ];
            expect(getHighestVulnerabilitySeverity(allSeverityVulns)).toEqual(CRITICAL);
        });

        it('returns UNKNOWN_VULNERABILITY_SEVERITY if no valid vulnerabilities are found', () => {
            const vulnerabilities = [];
            expect(getHighestVulnerabilitySeverity(vulnerabilities)).toEqual(
                'UNKNOWN_VULNERABILITY_SEVERITY'
            );

            const bogusVulns = [makeVuln('bogus', '', 0, '')];
            expect(getHighestVulnerabilitySeverity(bogusVulns)).toEqual(
                'UNKNOWN_VULNERABILITY_SEVERITY'
            );
        });
    });

    describe('getAnyVulnerabilityIsFixable', () => {
        it('returns true if the image has any fixable vulnerabilities', () => {
            const vulnerabilities = [
                makeVuln('', '', 0, ''),
                makeVuln('', '1.0.0', 0, ''),
                makeVuln('', '', 0, ''),
            ];
            expect(getIsSomeVulnerabilityFixable(vulnerabilities)).toEqual(true);
        });

        it('returns false if the image has no fixable vulnerabilities', () => {
            const vulnerabilities = [makeVuln('', '', 0, ''), makeVuln('', '', 0, '')];
            expect(getIsSomeVulnerabilityFixable(vulnerabilities)).toEqual(false);
        });
    });

    describe('getHighestCvssScore', () => {
        it('returns the highest CVSS score and the corresponding score version of any vulnerability in the image', () => {
            const vulnerabilities = [
                makeVuln('', '', 5.0, '3.0'),
                makeVuln('', '', 3.0, '3.0'),
                makeVuln('', '', 4.0, '2.0'),
                makeVuln('', '', 6.0, '2.0'),
                makeVuln('', '', 1.0, '3.0'),
            ];
            expect(getHighestCvssScore(vulnerabilities)).toEqual({
                cvss: 6.0,
                scoreVersion: '2.0',
            });
        });
    });

    describe('getEarliestDiscoveredAtTime', () => {
        it('returns the earliest discoveredAt time from the vulnerabilities', () => {
            const vulnerabilities: NonEmptyArray<{ discoveredAtImage: string }> = [
                { discoveredAtImage: '2021-01-01T00:00:00Z' },
                { discoveredAtImage: '2021-01-02T00:00:00Z' },
                { discoveredAtImage: '2021-01-03T00:00:00Z' },
            ];
            expect(getEarliestDiscoveredAtTime(vulnerabilities)).toEqual('2021-01-01T00:00:00Z');
        });
    });

    describe('formatCveDiscoveredTime', () => {
        it('formats "after" condition correctly', () => {
            expect(formatCveDiscoveredTime('>2024-01-01')).toEqual('After Jan 01, 2024');
        });

        it('formats "before" condition correctly', () => {
            expect(formatCveDiscoveredTime('<2024-12-31')).toEqual('Before Dec 31, 2024');
        });

        it('formats date without condition as "on"', () => {
            expect(formatCveDiscoveredTime('2024-06-15')).toEqual('On Jun 15, 2024');
        });

        it('returns original value for invalid date', () => {
            expect(formatCveDiscoveredTime('>invalid-date')).toEqual('>invalid-date');
        });

        it('returns original value for empty string', () => {
            expect(formatCveDiscoveredTime('')).toEqual('');
        });

        it('handles multiple condition characters', () => {
            expect(formatCveDiscoveredTime('>>2024-01-01')).toEqual('After Jan 01, 2024');
        });

        it('does not throw errors and returns original value for malformed input', () => {
            expect(() => formatCveDiscoveredTime('>2024-13-50')).not.toThrow();
            expect(formatCveDiscoveredTime('>2024-13-50')).toEqual('>2024-13-50');
        });
    });
});
